1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package com.google.common.io;
18
19 import com.google.common.annotations.Beta;
20 import com.google.common.annotations.VisibleForTesting;
21
22 import java.io.ByteArrayInputStream;
23 import java.io.ByteArrayOutputStream;
24 import java.io.File;
25 import java.io.FileInputStream;
26 import java.io.FileOutputStream;
27 import java.io.IOException;
28 import java.io.InputStream;
29 import java.io.OutputStream;
30
31
32
33
34
35
36
37
38
39
40 @Beta
41 public final class FileBackedOutputStream extends OutputStream {
42
43 private final int fileThreshold;
44 private final boolean resetOnFinalize;
45 private final ByteSource source;
46
47 private OutputStream out;
48 private MemoryOutput memory;
49 private File file;
50
51
52 private static class MemoryOutput extends ByteArrayOutputStream {
53 byte[] getBuffer() {
54 return buf;
55 }
56
57 int getCount() {
58 return count;
59 }
60 }
61
62
63 @VisibleForTesting synchronized File getFile() {
64 return file;
65 }
66
67
68
69
70
71
72
73
74
75 public FileBackedOutputStream(int fileThreshold) {
76 this(fileThreshold, false);
77 }
78
79
80
81
82
83
84
85
86
87
88
89
90 public FileBackedOutputStream(int fileThreshold, boolean resetOnFinalize) {
91 this.fileThreshold = fileThreshold;
92 this.resetOnFinalize = resetOnFinalize;
93 memory = new MemoryOutput();
94 out = memory;
95
96 if (resetOnFinalize) {
97 source = new ByteSource() {
98 @Override
99 public InputStream openStream() throws IOException {
100 return openInputStream();
101 }
102
103 @Override protected void finalize() {
104 try {
105 reset();
106 } catch (Throwable t) {
107 t.printStackTrace(System.err);
108 }
109 }
110 };
111 } else {
112 source = new ByteSource() {
113 @Override
114 public InputStream openStream() throws IOException {
115 return openInputStream();
116 }
117 };
118 }
119 }
120
121
122
123
124
125
126
127 public ByteSource asByteSource() {
128 return source;
129 }
130
131 private synchronized InputStream openInputStream() throws IOException {
132 if (file != null) {
133 return new FileInputStream(file);
134 } else {
135 return new ByteArrayInputStream(
136 memory.getBuffer(), 0, memory.getCount());
137 }
138 }
139
140
141
142
143
144
145
146
147 public synchronized void reset() throws IOException {
148 try {
149 close();
150 } finally {
151 if (memory == null) {
152 memory = new MemoryOutput();
153 } else {
154 memory.reset();
155 }
156 out = memory;
157 if (file != null) {
158 File deleteMe = file;
159 file = null;
160 if (!deleteMe.delete()) {
161 throw new IOException("Could not delete: " + deleteMe);
162 }
163 }
164 }
165 }
166
167 @Override public synchronized void write(int b) throws IOException {
168 update(1);
169 out.write(b);
170 }
171
172 @Override public synchronized void write(byte[] b) throws IOException {
173 write(b, 0, b.length);
174 }
175
176 @Override public synchronized void write(byte[] b, int off, int len)
177 throws IOException {
178 update(len);
179 out.write(b, off, len);
180 }
181
182 @Override public synchronized void close() throws IOException {
183 out.close();
184 }
185
186 @Override public synchronized void flush() throws IOException {
187 out.flush();
188 }
189
190
191
192
193
194 private void update(int len) throws IOException {
195 if (file == null && (memory.getCount() + len > fileThreshold)) {
196 File temp = File.createTempFile("FileBackedOutputStream", null);
197 if (resetOnFinalize) {
198
199
200 temp.deleteOnExit();
201 }
202 FileOutputStream transfer = new FileOutputStream(temp);
203 transfer.write(memory.getBuffer(), 0, memory.getCount());
204 transfer.flush();
205
206
207 out = transfer;
208 file = temp;
209 memory = null;
210 }
211 }
212 }